package com.i.template.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.i.template.config.MQConfig;
import com.i.template.constant.RedisConstants;
import com.i.template.constant.ResponseResult;
import com.i.template.domain.UserDO;
import com.i.template.domain.vo.PageVO;
import com.i.template.domain.vo.UserLogin;
import com.i.template.domain.vo.UserVO;
import com.i.template.service.IUserService;
import com.i.template.util.FileUtils;
import com.i.template.util.UserHolder;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.Email;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author lewis
 * @since 2024/7/24
 * @type class
 * @description 用户 控制层
 */
@RequiredArgsConstructor
@RestController
@Slf4j
@RequestMapping("/user")
@Tag(name = "用户管理", description = "用于管理用户信息")
public class UserController {
    private final IUserService userService;
    private final RabbitTemplate rabbitTemplate;
    private final RedisTemplate<String,Object> redisTemplate;
    /**
     * @param email
     * @return
     */
    @GetMapping("/code/{email}")
    @Operation(summary = "获取验证码",description = "根据email获取验证码")
    @ApiResponse(responseCode = "-1", description = "发送验证码成功")
    public ResponseResult<Boolean> getCode(@Email(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") @PathVariable String email) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(MQConfig.EXCHANGE_NAME,MQConfig.ROUTING_KEY,email,correlationData);
        // message -> {
        //   message.getMessageProperties().setExpiration("5000");
        //   return message;
        // }
        return ResponseResult.ok(true);
    }
    @PostMapping("/register")
    @Operation(summary = "用户注册",description = "用户输入必要信息,注册账户")
    @ApiResponse(responseCode = "-1", description = "用户注册成功")
    @ApiResponse(responseCode = "-2",description = "用户注册失败")
    public ResponseResult<Boolean> register(@RequestBody UserVO userVO){
        String s = String.valueOf(redisTemplate.opsForValue().get(RedisConstants.REGISTER_CODE_KEY_PREFIX + userVO.getEmail()));
        if(StringUtils.isEmpty(s) && !Objects.equals(userVO.getCaptcha(), s)) {
            return ResponseResult.fail("验证码错误!!!");
        }
        UserDO userDO = new UserDO();
        log.info(userVO.toString());
        BeanUtil.copyProperties(userVO,userDO);
        return ResponseResult.judge(userService.save(userDO));
    }
    @PostMapping("/login")
//    @Operation(summary = "用户登录",description = "输入邮箱和密码登录")
//    @ApiResponse(responseCode = "-1", description = "用户成功登录")
//    @ApiResponse(responseCode = "-2",description = "用户登录失败")
    public ResponseResult<UserVO> login(@RequestBody UserLogin userLogin){
        // TODO: 登陆时将token user信息使用HASH 存到redis中 设置有效期 查的时候就不必再去查询mysql了
        UserVO login = userService.login(userLogin);
        if(ObjectUtils.isEmpty(login)) {
            return ResponseResult.fail("登录失败!");
        }
        Map<String, Object> map = BeanUtil.beanToMap(login);
        redisTemplate.opsForHash().putAll(RedisConstants.USER_LOGIN_PREFIX + login.getUid(),map);
        redisTemplate.expire(RedisConstants.USER_LOGIN_PREFIX + login.getUid(), Duration.ofDays(30));
        return ResponseResult.ok(login);
    }
    @PostMapping("/save")
    @Operation(summary = "管理员添加用户")
    @ApiResponse(responseCode = "-1", description = "添加用户成功")
    @ApiResponse(responseCode = "-2",description = "添加用户失败")
    public ResponseResult<Boolean> insertUser(@RequestBody UserVO userVO){
        return ResponseResult.judge(userService.save(BeanUtil.copyProperties(userVO,UserDO.class)));
    }
    @GetMapping
    public ResponseResult<List<UserVO>> listByUser() {
        Object strVal = redisTemplate.opsForValue().get(RedisConstants.USER_ALL_PREFIX);
        if (strVal != null) {
            List<UserVO> list = JSONUtil.toList(JSONUtil.parseArray(String.valueOf(strVal)), UserVO.class);
            return ResponseResult.ok(list);
        }
        List<UserDO> list = userService.list();
        List<UserVO> userVOS = BeanUtil.copyToList(list, UserVO.class);
        redisTemplate.opsForValue().set(RedisConstants.USER_ALL_PREFIX,JSONUtil.toJsonStr(userVOS));
        return ResponseResult.ok(userVOS);
    }
    @GetMapping("/{uid}")
    @Operation(summary = "用户获取个人信息")
    @ApiResponse(responseCode = "-1", description = "获取个人信息成功")
    @ApiResponse(responseCode = "-2",description = "获取个人信息失败")
    public ResponseResult<UserVO> getUserById(@PathVariable Long uid) {
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(RedisConstants.USER_LOGIN_PREFIX + uid);
        if(MapUtil.isEmpty(entries)) {
            UserDO userDO = userService.query().eq("uid", uid).one();
            UserVO userVO = BeanUtil.copyProperties(userDO,UserVO.class);
            Map<String, Object> map = BeanUtil.beanToMap(userVO);
            redisTemplate.opsForHash().putAll(RedisConstants.USER_LOGIN_PREFIX + userVO.getUid(),map);
            return ResponseResult.ok(userVO);
        }
        UserVO userVO = BeanUtil.fillBeanWithMap(entries, new UserVO(), false);
        return ResponseResult.ok(userVO);
    }
    @GetMapping("/info")
    public ResponseResult<UserVO> getUserInfoById() {
        Long userId = UserHolder.getUser();
        System.out.println(userId);
        Map<Object, Object> entries = redisTemplate.opsForHash()
                .entries(RedisConstants.USER_LOGIN_PREFIX + userId);
        if(MapUtil.isEmpty(entries)) {
            UserDO userDO = userService.query().eq("uid", userId).one();
            UserVO userVO = BeanUtil.copyProperties(userDO,UserVO.class);
            Map<String, Object> map = BeanUtil.beanToMap(userVO);
            redisTemplate.opsForHash().putAll(RedisConstants.USER_LOGIN_PREFIX + userVO.getUid(),map);
            return ResponseResult.ok(userVO);
        }
        UserVO userVO = BeanUtil.fillBeanWithMap(entries, new UserVO(), false);
        return ResponseResult.ok(userVO);
    }
    @PutMapping
    @Operation(summary = "更新用户信息", description = "根据用户ID更新用户的信息")
    @ApiResponse(responseCode = "-1", description = "成功更新用户")
    @ApiResponse(responseCode = "-2",description = "更新用户失败")
    public ResponseResult<Boolean> updateUserById(@RequestBody UserVO userVO) {
        Long userId = UserHolder.getUser();
        redisTemplate.delete(RedisConstants.USER_LOGIN_PREFIX + userId);
        boolean isUpdated = userService.updateById(BeanUtil.copyProperties(userVO,UserDO.class));
        return ResponseResult.judge(isUpdated);
    }
    @PutMapping("/update")
    public ResponseResult<Boolean> updateUserByEmail(@RequestBody UserVO userVO) {
        Long userId = UserHolder.getUser();
        redisTemplate.delete(RedisConstants.USER_LOGIN_PREFIX + userId);
        boolean isUpdated = userService.updateById(BeanUtil.copyProperties(userVO,UserDO.class));
        return ResponseResult.judge(isUpdated);
    }
    @DeleteMapping("/{uid}")
    @Operation(summary = "删除用户", description = "根据用户ID删除用户")
    @ApiResponse(responseCode = "-1", description = "成功删除用户")
    @ApiResponse(responseCode = "-2",description = "删除用户失败")
    public ResponseResult<Boolean> deleteUser(@PathVariable Long uid) {
        QueryWrapper<UserDO> userDOQueryWrapper = new QueryWrapper<>();
        redisTemplate.delete(RedisConstants.USER_LOGIN_PREFIX + uid);
        userDOQueryWrapper.lambda().eq(UserDO::getUid,uid);
        return ResponseResult.judge(userService.remove(userDOQueryWrapper));
    }
    @PostMapping("/upload")
    @Operation(summary = "上传图片", description = "用户上传头像")
    @ApiResponse(responseCode = "-1", description = "用户头像上传成功")
    @ApiResponse(responseCode = "-2",description = "用户头像上传失败")
    public ResponseResult<String> upload(@RequestPart MultipartFile file){
        String avatarSrc = FileUtils.saveAvatar(UserHolder.getUser(), file);
        redisTemplate.delete(RedisConstants.USER_LOGIN_PREFIX + UserHolder.getUser());
        return ResponseResult.ok(avatarSrc);
    }
    @PostMapping("/page")
    public ResponseResult<PageVO<UserVO>> pageUserAll(@RequestBody PageVO<Void> pageVO) {
        PageVO<UserVO> userDOPageVO = userService.pageByUserInfo(pageVO.getCurrent(), pageVO.getSize());
        return ResponseResult.ok(userDOPageVO);
    }
    @GetMapping("/signIn")
    public void signIn() {
        userService.sign();
    }
    @GetMapping("/signInStatus")
    public ResponseResult<Boolean> signInStatus() {
        return ResponseResult.ok(userService.signInStatus());
    }
    @GetMapping("/visitCount")
    public ResponseResult<Long> getVisitCount() {
        return ResponseResult.ok(redisTemplate.opsForHyperLogLog().size(RedisConstants.VISIT_COUNT));
    }
    @DeleteMapping("/visitClear")
    public ResponseResult<?> clearVisit() {
        redisTemplate.opsForHyperLogLog().delete(RedisConstants.VISIT_COUNT);
        return ResponseResult.fail();
    }
    @GetMapping("/musician")
    public ResponseResult<List<UserVO>> queryWithMusician() {
        List<UserDO> userDOS = userService.lambdaQuery().eq(UserDO::getIsMusician, 1).list();
        List<UserVO> userVOS = BeanUtil.copyToList(userDOS, UserVO.class);
        return ResponseResult.ok(userVOS);
    }
    @GetMapping("/count")
    public ResponseResult<Long> countByUser() {
        Long count = userService.count();
        return ResponseResult.ok(count);
    }
    @GetMapping("/gender")
    public ResponseResult<?> genderStatistics() {
        return ResponseResult.ok(userService.genderStatistics());
    }
    @GetMapping("/export")
    public void exportWithUser(HttpServletResponse response) {
        List<UserDO> list = userService.list();
        ExcelWriter writer = ExcelUtil.getWriter();
        writer.merge(9,"用户信息");
        writer.write(list,true);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+System.currentTimeMillis()+".xlsx");
        ServletOutputStream out= null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            writer.flush(out, true);
            writer.close();
            IoUtil.close(out);
        }
    }
}
